'''
Created on 2009-10-8

@author: ray
'''

from threading import Thread, Lock
from Protocol import *
from socket import *
import os
import sys
import time

MAX_UDPBUF = 16384
MAX_TCPBUF = 65536/2
FILE_SAVE_DIR = ""

def getFileSize(file):
    if os.path.isdir(file):
        return 0L
    else:
        f = open(file, "rb")
        f.seek(0,os.SEEK_END)
        size = f.tell()
        f.close()
        return size

def xstrToLong(xstr):
    try:
        return long(xstr, 16)
    except Exception, e:
        print e
        return 0L


class TCPThread(Thread):
    ''' This Thread is responsible for listenning and assignning
    a SendFileThread to client for transfering files'''
    
    def __init__(self, ipmsg):
        Thread.__init__(self)
        self.ipmsg = ipmsg

    def run(self):
        while True:
            sock, addr = self.ipmsg.tcp_sd.accept()
            data = sock.recv(MAX_TCPBUF)
            msg = self.ipmsg.resolveMsg(data, addr)
            if msg.command == IPMSG_GETFILEDATA:
                type = IPMSG_FILE_REGULAR
                packetNo, id, offset = [xstrToLong(xstr) for xstr in msg.msg.split(":")[:3]]
            elif msg.command == IPMSG_GETDIRFILES:
                type = IPMSG_FILE_DIR
                packetNo, id = [xstrToLong(xstr) for xstr in msg.msg.split(":")[:2]]
                offset = 0
            file = self.ipmsg.delSendFile((packetNo, id))
            SendFilesThread(self.ipmsg, sock, file, type, offset).start()
        

class UDPThread(Thread):
    ''' Thread used for listenning upcoming events'''
    
    def __init__(self, msgmng):
        Thread.__init__(self)
        self.msgmng = msgmng

    def run(self):
        while True:
            data, addr = self.msgmng.udp_sd.recvfrom(MAX_UDPBUF)
            print (data, addr) #debug print
            if not self.msgmng.msgQueue.full():
                self.msgmng.msgQueue.put((data, addr))
            else:
                print "message queue is full,message discarded"


class DispatchThread(Thread):
    ''' Thread used for dispatching specific events to handlers '''
    
    def __init__(self, msgmng):
        Thread.__init__(self)
        self.msgmng = msgmng
        
    def run(self):
        while True:
            (data, addr) = self.msgmng.msgQueue.get(block=True)
            msg = self.msgmng.resolveMsg(data, addr)
            if not msg:
                print "Packet error!"
            else:
                self.msgmng.udpEvent(msg)
                
                
class RecvFilesThread(Thread):
    ''' Thread for receiving files '''
    
    def __init__(self, ipmsg, msg, files, savedir=FILE_SAVE_DIR):
        Thread.__init__(self)
        self.ipmsg = ipmsg
        self.files = files
        self.msg   = msg
        self.savedir = savedir
        
    def run(self):
        for f in self.files:
            if f.type == IPMSG_FILE_REGULAR:
                self.recvFile(f)
            else:
                self.recvFolder(f)
        self.ipmsg.informRecvDone()
        
    def recvFile(self, file):
        self.sock = socket(AF_INET, SOCK_STREAM)
        self.sock.connect((self.msg.host.addr, self.msg.host.portNo))
        localfile = open(os.path.join(self.savedir, file.name), "wb")
        msg = "%x:%x:%x" %(self.msg.packetNo, file.id, 0)
        self.ipmsg.tcpSend(self.sock, IPMSG_GETFILEDATA, msg, "")
        recvLen = 0
        while recvLen < file.size:
            data = self.sock.recv(MAX_TCPBUF)
            recvLen += len(data)
            self.ipmsg.informRecvPercent(file.name, recvLen, file.size)
            if data:
                localfile.write(data)
        localfile.close()
        self.sock.close()
    
    def recvFolder(self, folder):
        self.sock = socket(AF_INET, SOCK_STREAM)
        self.sock.connect((self.msg.host.addr, self.msg.host.portNo))
        msg = "%x:%x:%x" %(self.msg.packetNo, folder.id, 0)
        self.ipmsg.tcpSend(self.sock, IPMSG_GETDIRFILES, msg, "")
        self.constructDir(self.savedir)
        self.sock.close()
    
    def getHead(self):
        ''' Currently ignore extra attr'''
        headsize = self.sock.recv(5)
        head = self.sock.recv(xstrToLong(headsize[:4])-5)
        name, size, type, exAttrs = head.split(":", 3)
        return name, xstrToLong(size), xstrToLong(type), exAttrs
    
    def writeFile(self, name, dir, size):
        ''' Anyone tell me how to set files' READ/WRITE/HIDE attributes? '''
        p = os.path.join(dir, name)
        f = open(p, "wb")
        l = 0
        while l < size:
            data = self.sock.recv(min(size - l, MAX_TCPBUF))
            l += len(data)
            self.ipmsg.informRecvPercent(p, l, size)
            if data:
                f.write(data)
        f.close()
        
    def setFileAttrs(self, path, exAttrs):
        ''' left blank '''
        pass
          
    def constructDir(self, saveDir):
        saveDir = saveDir
        curDir = saveDir
        while True:
            name, size, type, _ = self.getHead()
            if GET_MODE(type) == IPMSG_FILE_REGULAR :
                self.writeFile(name, curDir, size)
            elif GET_MODE(type) == IPMSG_FILE_DIR:
                curDir = os.path.join(curDir, name)
                os.mkdir(curDir)
            elif GET_MODE(type) == IPMSG_FILE_RETPARENT:
                curDir = os.path.split(curDir)[0]
            else:
                pass # Other file types are not supported yet
            if GET_MODE(type) == IPMSG_FILE_RETPARENT and curDir == saveDir:
                break


class SendFilesThread(Thread):
    ''' Thread for sending files '''
    
    def __init__(self, ipmsg, sock, file, type, offset=0):
        Thread.__init__(self)
        self.ipmsg = ipmsg
        self.sock = sock
        self.file = file
        self.type = type
        self.offset = offset
        
    def run(self):
        if self.type == IPMSG_FILE_REGULAR:
            self.sendFile(self.file)
        elif self.type == IPMSG_FILE_DIR:
            self.sendFolder(self.file)
        self.sock.close()  
        
    def sendFile(self, file):
        f = open(file,"rb")
        while True:
            data = f.read(MAX_TCPBUF)
            if not data:
                break
            else:
                self.sock.send(data)
        f.close()
        self.ipmsg.informSendDone(file)
        
    def sendFolder(self, dir):
        self.sendHead(dir)
        if os.path.isdir(dir):
            for f in os.listdir(dir):
                p = os.path.join(dir, f)
                self.sendFolder(p)
            self.sendHead(".")
        elif os.path.isfile(dir) or os.path.islink(dir):
            self.sendFile(dir)
    
    def sendHead(self, dir):
        if dir == ".":
            name = dir
            size = 0
            type = IPMSG_FILE_RETPARENT
        else:
            if os.path.isdir(dir):
                type = IPMSG_FILE_DIR
            elif os.path.isfile(dir):
                type = IPMSG_FILE_REGULAR
            elif os.path.islink(dir):
                type = IPMSG_FILE_SYMLINK
            name = os.path.basename(dir)
            size = getFileSize(dir)
        exAttr = self.getExtraAttr(dir)
        head = ":%s:%x:%x%s:" %(name, size, type, exAttr)
        headsize = len(head) + 4
        msg = "%04x%s" %(headsize, head)
        self.sock.send(msg)
    
    def getExtraAttr(self, path):
        stat = os.lstat(path)
        return ":%x=%x:%x=%x" %(IPMSG_FILE_MTIME, stat.st_mtime, IPMSG_FILE_CREATETIME, stat.st_ctime)
        
        
        
        
        
        
